Route map, role model, component inventory and data contracts — for sign-off before any screen gets built. Built on your uploaded clean-room roleplay shell, styled only with design-system tokens so the palette stays an open decision.
Fan: invite → apply → wait → approved → first session. Reads end-to-end in the prototype as one continuous click-path.
Creator dashboard · Server home · Character card management · Extended roleplay page. Fan-flow screens ride along because the journey needs them.
One language at a time, switched from the header (EN / 中文) — never both on screen. Every string ships as an EN/ZH pair, so the switch is a data change, not a screen fork.
Immersive: messages float over the character art, exactly as your upload does. Rail, right panel and mobile drawer preserved.
List + detail split on desktop, full-height drawer on mobile. Full trust & safety panel inside the detail column.
Invite code, creator questions, rules agreement, moderator approval, membership expiry, per-character grants — all mandatory. External account link becomes per-server optional.
One states gallery screen holds every loading / empty / error / denied / expired / revoked variant, so product screens stay clean.
Light-blue accent, applied as a token retune only: the ramp is overridden at :root and every surface still reads var(--color-*). Swapping the theme stays a token edit, not a screen edit.
Three-column grid, panel open/closed/auto tri-state, profile ⇄ chat mode switch, 980px drawer breakpoint, 640px bottom-nav breakpoint, reduced-motion guard. All of it stays; the CSS variables at the top of globals.css are re-pointed at design-system tokens.
Composer (view) + useSession() (future API seam)No names, art, descriptions or messages carried over from any third-party product. Every character, server and message in the prototype is invented placeholder content, and art is a drawn placeholder rather than an image.
?next= so an invite link survives sign-inOut of scope for the first prototype pass — routes reserved so the role model stays complete.
Code valid, unexpired, under application cap.
Signed in and verified.
Per-server setting — required, optional, or off. Generic provider slots.
Creator questions answered, rules agreed, source recorded.
Owner or moderator decides. Then membership, group, expiry and per-character grants.
| Nav item | Admin | Owner | Moderator | Member | Applicant | Banned |
|---|---|---|---|---|---|---|
| Home 首页 | ● | ● | ● | ● | — | — |
| My servers 我的服务器 | ● | ● | ● | ● | — | — |
| Characters 角色 | ● | ● | ● | ● granted only | — | — |
| Sessions 会话 | ● | ● | ● | ● | — | — |
| Applications 申请 | ● | ● | ● | ● | ● | ● read-only |
| Account settings 账号设置 | ● | ● | ● | ● | ● | ● |
| Studio overview | ● | ● | — | — | — | — |
| Character cards | ● | ● | ● edit, no publish | — | — | — |
| Applications review | ● | ● | ● | — | — | — |
| Members | ● | ● | ● no ban | — | — | — |
| Invitations | ● | ● | ● | — | — | — |
| Announcements | ● | ● | ● | — | — | — |
| Server settings | ● | ● | — | — | — | — |
type Role = 'admin' | 'owner' | 'moderator' | 'member'
| 'applicant' | 'rejected' | 'banned'
type AppStatus = 'draft' | 'pending' | 'info_required'
| 'approved' | 'rejected' | 'expired' | 'revoked'
interface Server {
id, slug, name, tagline, coverRef
creator: CreatorRef // verified: boolean
rules: string[]
externalAccount: 'required' | 'optional' | 'off'
questions: Question[]
memberGroups: MemberGroup[]
}
interface Invitation {
code, serverId, createdBy
expiresAt, maxApplications, usedCount
singleUse: boolean
requiredProvider?: ProviderId
defaultGroupId?: string
disabled: boolean
}
interface Application {
id, serverId, applicantId, invitationCode
status: AppStatus
displayName, reason, referrer?
answers: Answer[]
externalAccount?: ExternalAccountRef
risk: RiskSignal[] // server-computed
notes: ReviewNote[] // staff-only
submittedAt, decidedAt?, decidedBy?
}
interface CharacterCard {
id, serverId, name, blurb, avatarRef, coverRef
tags: string[]
visibility: 'draft' | 'published' | 'archived'
groupIds: string[]
version: number
hasSystemPrompt: boolean // never the prompt itself
promptRef: string // opaque server handle
model: ModelSettings // display-safe subset
}
One lib/api/*.ts module per resource, each exporting async functions with the real signature and a mock implementation behind USE_MOCKS. Screens import the function, never the fixture. Swapping to real endpoints touches only these files.
Composer and MessageList are pure views over useSession(), which today appends to local state and returns a canned reply. A streaming transport drops in behind the same hook.
System prompts, world info bodies and model keys never enter a client payload. The editor sends patches and shows a redacted summary — "12 entries · last edited 3d ago" — plus a character count.
Landing → invitation → application → status (pending → approved) → enter server. Clickable straight through.
Server home with announcement, character collection, recent sessions, membership status.
Your shell, extended: server + character switchers, sessions, model placeholder, settings drawer, working composer.
Dashboard, character card editor with six form sections, review split view, invitations, members.
Gallery of all ten mock states plus the shared empty / skeleton / denied components.